package session

import (
	"sync"
	"errors"

	"gitee.com/wint/tge"
)

// 用户数据接口
type UserData interface {
	GetUserId() interface{}
	OnClose() 	// 网络链接被断开
}

// 用户连接会话
type Session struct {
	sync.RWMutex	// 线程锁

	UserData 	// 玩家数据

	sendPrinter	func(msg interface{}) 	// 发送数据时的打印方法

	userId 	interface{}
	isClose	bool

	groups 	map[string]string 	// 所加入的消息群组

	agent 		tge.Agent

	mgr			*SessionManager 	// 会话管理器

	sesId 		int32		// 会话id，默认为 0，需要主动设置

	From  		string		// 来源信息
}

func NewSession() *Session {
	ses := &Session{}
	ses.isClose = true
	ses.groups = make(map[string]string)
	ses.mgr = mgr
	return ses
}

func (s *Session) Send(msg interface{}) (int, error) {
	s.RLock()
	defer s.RUnlock()

	if s.isClose || nil == s.agent {
		return 0, errors.New("session is close")
	}

	if nil != s.sendPrinter {
		s.sendPrinter(msg)
	}

	return s.agent.WriteMsg(msg)
}

func (s *Session) SendBuf(msg []byte) (int, error) {
	return s.Write(msg)
}

// writer 接口实现
func (s *Session) Write(b []byte) (int, error) {
	s.RLock()
	defer s.RUnlock()

	if s.isClose || nil == s.agent {
		return 0, errors.New("session is close")
	}

	return s.agent.Write(b)
}

func (s *Session) SendBufWithBase64(buf []byte, buf64 []byte) {
	s.RLock()
	defer s.RUnlock()

	if s.isClose || nil == s.agent {
		return
	}

	s.agent.WriteWithBase64(buf, buf64)
}

// 获取用户的唯一识别 id
func (s *Session) GetUserId() interface{} {
	s.RLock()
	defer s.RUnlock()

	return s.userId
}

func (s *Session) SetUserData(d UserData) {
	if id, ok := s.setUserData(d); ok {
		if nil != s.mgr {
			if nil != id {
				// s.mgr.RemovePlayer(id)
				s.mgr._removePlayer(id)
			}
			if nil != d && !s.isClose {
				s.mgr.AddPlayer(s)
			}
		}
	}
}

func (s *Session) setUserData(d UserData) (interface{}, bool) {
	s.Lock()
	defer s.Unlock()

	if s.UserData == d {
		return nil, false
	}

	s.UserData = d

	oldUserId := s.userId
	if nil != s.UserData {
		s.userId = s.UserData.GetUserId()
	} else {
		s.userId = nil
	}
	if oldUserId == s.userId {
		oldUserId = nil
	}
	return oldUserId, true
}

func (s *Session) GetUserData() UserData {
	s.RLock()
	defer s.RUnlock()

	return s.UserData
}

func (s *Session) SetSesId(id int32) {
	s.Lock()
	defer s.Unlock()
	s.sesId = id
}

func (s *Session) GetSesId() int32 {
	s.RLock()
	defer s.RUnlock()

	return s.sesId
}

func (s *Session) Addr() string {
	s.RLock()
	defer s.RUnlock()

	if nil != s.agent {
		return s.agent.Addr()
	}

	return ""
}

func (s *Session) IsClose() bool {
	s.RLock()
	defer s.RUnlock()
	return s.isClose
}

// 由 agent 关闭引起的关闭
func (s *Session) Close() {
	s.Lock()
	s.agent = nil
	s.isClose = true
	if nil != s.UserData {
		s.UserData.OnClose()
	}
	s.Unlock()

	if nil != s.userId && nil != s.mgr {
		s.mgr.removeSession(s, s.userId)
	}
}

// 主动关闭连接
func (s *Session) DoClose() {
	s.doClose()

	if nil != s.userId && nil != s.mgr {
		s.mgr.removeSession(s, s.userId)
	}
}

func (s *Session) doClose() {
	s.Lock()

	if s.agent != nil {
		a := s.agent
		s.agent = nil
		defer a.Close()
		defer a.SetUserData(nil)
	}

	defer s.Unlock()
	s.isClose = true
}

func (s *Session) SetAgent(agent tge.Agent) {
	s.Lock()
	defer s.Unlock()

	s.agent = agent
	s.isClose = false
	agent.SetUserData(s)
}

func (s *Session) GetAgent() tge.Agent {
	s.RLock()
	defer s.RUnlock()

	return s.agent
}

func (s *Session) JoinGroup(name string) {
	if nil != s.mgr && !s.isClose {
		s.mgr.JoinGroup(s, name)
	}
}

func (s *Session) QuitGroup(name string) {
	if nil != s.mgr {
		s.mgr.QuitGroup(s, name)
	}
}

func (s *Session) QuitAllGroup() {
	if nil != s.mgr {
		s.mgr.QuitAllGroup(s)
	}
}

func (s *Session) addGroupName(name string) {
	s.groups[name] = name
}

func (s *Session) removeGroupName(name string) {
	delete(s.groups, name)
}

func (s *Session) clearGroupName() {
	if len(s.groups) > 0 {
		s.groups = make(map[string]string)
	}
}

func (s *Session) SetManager(m *SessionManager) {
	s.Lock()
	defer s.Unlock()

	s.mgr = m
}

func (s *Session) GetManager() *SessionManager {
	s.RLock()
	defer s.RUnlock()

	return s.mgr
}

func (s *Session) SetSendPrinter(fn func(msg interface{})) {
	s.Lock()
	defer s.Unlock()

	s.sendPrinter = fn
}